﻿using movie.tool.library.hash;
using movie.tool.library.utils;
using movie.tool.module.film.model;
using movie.tool.module.login;
using movie.tool.module.response;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Xml;

namespace movie.tool.module.http
{
    public class HttpInvoker
    {
        static HttpClient httpClient;
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        static HttpInvoker()
        {
            httpClient = new HttpClient();
            httpClient.Timeout = TimeSpan.FromHours(1);
            if (movie_tool.App.APP_FOR_TEST)
            {
                httpClient.BaseAddress = new Uri("https://hzsvn.iok.la/movie/");
            }
            else
            {
                httpClient.BaseAddress = new Uri("https://lwvigverse.com/movie/");
            }
        }
        public static void UpdateToken(string token)
        {
            httpClient.DefaultRequestHeaders.Authorization = token == null ? null : AuthenticationHeaderValue.Parse("Bearer " + token);
        }
        public static async Task<bool> PostAsync(string uri, HttpContent body)
        {
            string json = await DoPost(uri, body);
            if (json != null)
            {
                OperatorResponse resData = JsonConvert.DeserializeObject<OperatorResponse>(json, OperatorResponse.settings);
                if (resData != null && resData.Success)
                {
                    return true;
                }
                else
                {
                    TipMessage(resData);
                    return false;
                }
            }
            return false;
        }
        public static async Task<bool> PutAsync(string uri, HttpContent body)
        {
            string json = await DoPut(uri, body);
            if (json != null)
            {
                OperatorResponse resData = JsonConvert.DeserializeObject<OperatorResponse>(json, OperatorResponse.settings);
                if (resData != null && resData.Success)
                {
                    return true;
                }
                else
                {
                    TipMessage(resData);
                    return false;
                }
            }
            return false;
        }
        public static async Task<T> PutAsync<T>(string uri, HttpContent body)
        {
            string json = await DoPut(uri, body);
            if (!string.IsNullOrEmpty(json))
            {
                SingleDataResponse<T> resData = JsonConvert.DeserializeObject<SingleDataResponse<T>>(json, OperatorResponse.settings);
                if (resData.Success)
                {
                    return resData.Data;
                }
                else
                {
                    TipMessage(resData);
                }
            }
            return default(T);
        }
        public static async Task<bool> DeleteAsync(string uri)
        {
            string json = await DoDelete(uri);
            if (json != null)
            {
                OperatorResponse resData = JsonConvert.DeserializeObject<OperatorResponse>(json, OperatorResponse.settings);
                if (resData != null && resData.Success)
                {
                    return true;
                }
                else
                {
                    TipMessage(resData);
                    return false;
                }
            }
            return false;
        }
        private static void TipMessage(OperatorResponse resData)
        {
            string msg = resData != null ? resData.Message : null;
            MessageBox.Show(string.IsNullOrWhiteSpace(msg) ? "请求失败" : resData.Message);
        }
        /// <summary>
        /// 执行post请求并返回字符串
        /// </summary>
        /// <param name="uri">请求的uri</param>
        /// <param name="body">请求的主体内容</param>
        /// <returns>返回结果的字符</returns>
        private static async Task<string> DoPost(string uri, HttpContent body)
        {
            HttpResponseMessage res = await httpClient.PostAsync(uri, body);
            if (res.IsSuccessStatusCode)
            {
                return await res.Content.ReadAsStringAsync();
            }
            else
            {
                ProcessStatus(res);
                return null;
            }
        }
        private static async Task<string> DoPut(string uri, HttpContent body)
        {
            HttpResponseMessage res = await httpClient.PutAsync(uri, body);
            if (res.IsSuccessStatusCode)
            {
                return await res.Content.ReadAsStringAsync();
            }
            else
            {
                ProcessStatus(res);
                return null;
            }
        }
        /// <summary>
        /// 发送DELETE请求
        /// </summary>
        /// <param name="uri">请求的uri</param>
        /// <returns>请求结果</returns>
        private static async Task<string> DoDelete(string uri)
        {
            HttpResponseMessage res = await httpClient.DeleteAsync(uri);
            if (res.IsSuccessStatusCode)
            {
                return await res.Content.ReadAsStringAsync();
            }
            else
            {
                ProcessStatus(res);
                return null;
            }
        }

        private static void ProcessStatus(HttpResponseMessage res)
        {
            if (res.StatusCode == HttpStatusCode.Unauthorized)
            {
                UpdateToken(null);
                LoginPage.DeleteTokenFile();
                MainWindow.ShowLoginPage();
            }
            else
            {
                MessageBox.Show("请求失败:" + res.StatusCode);
            }
        }

        private static async Task<string> DoGet(string uri)
        {
            HttpResponseMessage res = await httpClient.GetAsync(uri);
            if (res.IsSuccessStatusCode)
            {
                return await res.Content.ReadAsStringAsync();
            }
            else
            {
                ProcessStatus(res);
                return null;
            }
        }
        public static async Task<T> PostAsync<T>(string uri, HttpContent body)
        {
            string json = await DoPost(uri, body);
            if(!string.IsNullOrEmpty(json))
            {
                SingleDataResponse<T> resData = JsonConvert.DeserializeObject<SingleDataResponse<T>>(json, OperatorResponse.settings);
                if (resData.Success)
                {
                    return resData.Data;
                }
                else
                {
                    TipMessage(resData);
                }
            }
            return default(T);
        }
        public static async Task<T> GetAsync<T>(string uri)
        {
            string json = await DoGet(uri);
            if (!string.IsNullOrEmpty(json))
            {
                SingleDataResponse<T> resData = JsonConvert.DeserializeObject<SingleDataResponse<T>>(json, OperatorResponse.settings);
                if (resData.Success)
                {
                    return resData.Data;
                }
                else
                {
                    TipMessage(resData);
                }
            }
            return default(T);
        }
        public static async Task<string> DownloadFileAsync(string uri,string filePath,bool isDir = true)
        {
            HttpResponseMessage resMsg = await httpClient.GetAsync(uri);
            if(resMsg != null)
            {
                if (resMsg.IsSuccessStatusCode)
                {
                    MediaTypeHeaderValue contentTypes = resMsg.Content.Headers.ContentType;
                    if(contentTypes != null)
                    {
                        if (string.Equals("application/json", contentTypes.MediaType))
                        {
                            string str = await resMsg.Content.ReadAsStringAsync();
                            OperatorResponse res = JsonConvert.DeserializeObject<OperatorResponse>(str);
                            if(res != null)
                            {
                                logger.Error("下载文件[{0}]失败：{1}", uri, res.Message);
                                MessageBox.Show(string.IsNullOrEmpty(res.Message) ? "下载失败" : res.Message);
                            }
                            else
                            {
                                logger.Error("下载文件[{0}]失败：{1}", uri, str);
                            }
                        }
                        else
                        {
                            string file = null;
                            if (isDir)
                            {
                                if (resMsg.Content.Headers.TryGetValues("Content-Disposition", out IEnumerable<string> cd))
                                {
                                    if (cd != null)
                                    {
                                        string fileName = null;
                                        foreach (string str in cd)
                                        {
                                            if (!string.IsNullOrEmpty(str))
                                            {
                                                string str1 = HttpUtility.UrlDecode(str);
                                                int pos = str1.LastIndexOf("filename=");
                                                if (pos > -1)
                                                {
                                                    fileName = str1.Substring(pos + "filename=".Length);
                                                    break;
                                                }
                                            }
                                        }
                                        if (string.IsNullOrEmpty(fileName))
                                        {
                                            MessageBox.Show("需要指定文件名");
                                        }
                                        else
                                        {
                                            file = Path.Join(filePath, fileName);
                                        }
                                    }
                                    else
                                    {
                                        MessageBox.Show("需要指定文件名");
                                    }
                                }
                                else
                                {
                                    MessageBox.Show("需要指定文件名");
                                }
                            }
                            else
                            {
                                file = filePath;
                            }
                            if (string.IsNullOrEmpty(file))
                            {
                                return string.Empty;
                            }
                            try
                            {
                                DirectoryInfo dir = Directory.GetParent(file);
                                if (dir != null)
                                {
                                    if (!dir.Exists)
                                    {
                                        dir.Create();
                                    }
                                }
                                using (Stream stream = File.OpenWrite(file))
                                {
                                    await resMsg.Content.CopyToAsync(stream);
                                    return file;
                                }
                            }
                            catch(Exception e)
                            {
                                logger.Error(e, "创建文件失败");
                            }
                        }
                    }
                    else
                    {
                        logger.Error("未知类型,uri={0}", uri);
                    }
                }
            }
            return string.Empty;
        }
        /// <summary>
        /// 下载文件。在下载之前先比较本地是否存在
        /// </summary>
        /// <param name="downloadUri">下载文件的uri</param>
        /// <param name="serverDigest">服务端的文件摘要</param>
        /// <param name="reletivePath">文件相对目录</param>
        /// <param name="searchRootPath">搜索的根目录。如果在此目录下,有reletivePath路径的文件,并且与服务端摘要相同,则使用此文件而不用下载</param>
        /// <param name="storePath">下载的文件存储的根目录。最终文件存储目录为此目录+relativePath</param>
        /// <returns>异步任务</returns>
        public static async Task DownloadFile(string downloadUri, string serverDigest, string reletivePath, string searchRootPath, string storePath)
        {
            bool download = true;
            string fileStorePath = Path.Join(storePath, reletivePath);
            if (File.Exists(fileStorePath))
            {
                string localDigest = SHA1Hash.FileDigest(fileStorePath);
                if (serverDigest.Equals(localDigest))
                {
                    //文件相同,不用下载,也不用复制
                    download = false;
                }
                else
                {
                    //文件不同，删除本地存在的文件
                    File.Delete(fileStorePath);
                }
            }
            else
            {
                DirectoryInfo dir = Directory.GetParent(fileStorePath);
                if (dir != null)
                {
                    if (!dir.Exists)
                    {
                        dir.Create();
                    }
                }
            }
            if (download)
            {
                //再看一下配置目录下有没有这个文件，有并且一样就可以复制过来
                string filePath = Path.Join(searchRootPath, reletivePath);
                if (File.Exists(filePath))
                {
                    string localDigest = SHA1Hash.FileDigest(filePath);
                    if (serverDigest.Equals(localDigest))
                    {
                        //文件相同,不用下载,复制过去就可以了
                        download = false;
                        File.Copy(filePath, fileStorePath);
                    }
                }
            }
            if (download)
            {
                await DownloadFileAsync(downloadUri, fileStorePath, false);
                string localDigest = SHA1Hash.FileDigest(fileStorePath);
                if (!object.Equals(serverDigest, localDigest))
                {
                    MessageBox.Show("下载文件[" + reletivePath + "]失败,检验结果与服务端不匹配");
                }
            }
        }

        public static async Task<bool> UploadObsFile(ObsSignedUrl url, string filePath)
        {
            FileInfo fileInfo = new FileInfo(filePath);
            long len = fileInfo.Length;
            if (len <= 100 * 1024 * 1024)
            {
                //100M以内单个文件上传
                return await UploadObsNormalFile(url, filePath);
            }
            else if (len < 5L * 1024 * 1024 * 10000)
            {
                //5万M以内,单个分段5M
                return await UploadLargeFile(url, filePath, 5 * 1024 * 1024);
            }
            else
            {
                //最大分段10000个，计算每个的大小,但是单个分段大小必须在5G以内.按实际情况,每个分段最大1G以内就可以了
                long s = len / 10000;
                if (s >= 1024 * 1024 * 1024)
                {
                    MessageBox.Show("文件大小[" + len + "]超过最大值");
                    return false;
                }
                return await UploadLargeFile(url, filePath, s);
            }
        }
        public static Task<bool> UploadObsNormalFile(ObsSignedUrl url, string filePath)
        {
            return Task.Factory.StartNew(() => {
                HttpWebRequest req = WebRequest.Create(url.Url) as HttpWebRequest;
                req.Method = "PUT";
                req.Timeout = 3600000; //1小时超时
                AddHeaders(url, req);
                req.SendChunked = true;
                req.AllowWriteStreamBuffering = false;
                using (Stream requestStream = req.GetRequestStream())
                {
                    byte[] buffer = new byte[8192];
                    using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        int len;
                        do
                        {
                            len = fileStream.Read(buffer, 0, 8192);
                            if (len > 0)
                            {
                                requestStream.Write(buffer, 0, len);
                            }
                            else
                            {
                                Console.WriteLine(len);
                            }
                        } while (len > 0);
                    }
                }
                HttpWebResponse response = req.GetResponse() as HttpWebResponse;
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    return true;
                }
                else
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        string res = Stream2Str(stream); ;
                        MessageBox.Show(res);
                        return false;
                    }
                }
            });
        }
        /// <summary>
        /// 上传大文件到OBS服务器
        /// </summary>
        /// <param name="url"></param>
        /// <param name="filePath">文件路径。文件必须存在，需要调用方判断</param>
        /// <returns></returns>
        public static async Task<bool> UploadLargeFile(ObsSignedUrl url, string filePath, long segmentSize)
        {
            InitPartRes initRes = await InitUploadPartFile(url);
            if(initRes == null)
            {
                MessageBox.Show("初始化分段上传任务失败");
                return false;
            }
            if(!initRes.Success && !string.IsNullOrWhiteSpace(initRes.message))
            {
                MessageBox.Show(initRes.message);
                return false;
            }
            FileInfo fi = new FileInfo(filePath);
            long len = fi.Length;
            long parts = len / segmentSize;
            if (len % segmentSize != 0)
            {
                parts++;
            }
            List<UploadPartRes> partEtags = new List<UploadPartRes>();
            string urlId = initRes.UrlId;
            ObsSignedUrl uploadUrl = null;
            for (int part = 1; part <= parts; ++part)
            {
                long contentLength = part == parts ? len - (part - 1) * segmentSize : segmentSize;
                uploadUrl = await SignUploadPart(urlId, part, initRes.UploadId);
                UploadPartRes partRes;
                int times = 5;
                bool retry;
                do
                {
                    partRes = await DoUploadPartFile(uploadUrl, filePath, part, segmentSize, contentLength);
                    retry = !partRes.Success && partRes.Retry && times > 0;
                    if (retry)
                    {
                        times--;
                        await Task.Delay(1000);
                    }
                } while (retry);
                if (!partRes.Success)
                {
                    //分段上传失败，结束上传
                    break;
                }
                else
                {
                    partEtags.Add(partRes);
                }
            }
            if (partEtags.Count == parts)
            {
                //成功了
                Retryable completeResult = await CompleteUploadFile(uploadUrl, initRes.UploadId, partEtags);
                if (!completeResult.Success)
                {
                    DeleteUploadPartFile(uploadUrl, initRes.UploadId);
                    if (string.IsNullOrWhiteSpace(completeResult.message))
                    {
                        MessageBox.Show("合并分段文件" + filePath + "失败");
                    }
                    else
                    {
                        MessageBox.Show("合并分段文件" + filePath + "失败:" + completeResult.message);
                    }
                    
                    return false;
                }
                return true;
            }
            else
            {
                logger.Error("分段上传文件结果异常：应该有{0}段,实际上只有{1}段", parts, partEtags.Count);
                DeleteUploadPartFile(uploadUrl, initRes.UploadId);
                MessageBox.Show("分段上传文件" + filePath + "失败");
                return false;
            }
        }

        private static async Task<InitPartRes> InitUploadPartFile(ObsSignedUrl url)
        {
            SignUrlParam param = new SignUrlParam();
            param.Method = "POST";
            param.SpecialParam = "UPLOADS";
            param.UrlId = url.Id;

            JsonContent content = JsonContent.Create(param);
            ObsSignedUrl initUrl = await PostAsync<ObsSignedUrl>("movieserver/obs/signedUrl", content);
            if(initUrl != null)
            {
                int times = 5;
                bool retry;
                InitPartRes res;
                do
                {
                    res = await DoInitPart(initUrl);
                    retry = res.Success == false && res.Retry && times > 0;
                    if (retry)
                    {
                        // wait a second
                        times--;
                        await Task.Delay(1000);
                    }
                } while (res == null || retry);
                return res;
            }
            return null;
        }

        private static Task<InitPartRes> DoInitPart(ObsSignedUrl url)
        {
            return Task.Factory.StartNew(() => {
                HttpWebRequest req = WebRequest.Create(url.Url) as HttpWebRequest;
                req.Method = "POST";
                req.Timeout = 3600000; //1小时超时
                AddHeaders(url, req);
                HttpWebResponse response = req.GetResponse() as HttpWebResponse;
                InitPartRes res = new InitPartRes();
                res.UrlId = url.Id;
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        string resp = Stream2Str(stream);
                        res.UploadId = XmlHelper.ParseInitPartUploadId(resp);
                        res.Success = true;
                    }
                    return res;
                }
                else if (response.StatusCode == HttpStatusCode.RequestTimeout)
                {
                    res.Retry = true;
                    return res;
                }
                else
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        string resp = Stream2Str(stream);
                        res.message = resp;
                    }
                    return res;
                }
            });
        }

        private static async Task<ObsSignedUrl> SignUploadPart(string urlId, int partNumber, string uploadId)
        {
            Dictionary<string, object> queryParams = new Dictionary<string, object>();
            queryParams.Add("partNumber", partNumber);
            queryParams.Add("uploadId", uploadId);

            SignUrlParam param = new SignUrlParam();
            param.Method = "PUT";
            param.UrlId = urlId;
            param.Params = queryParams;

            JsonContent content = JsonContent.Create(param);
            return await PostAsync<ObsSignedUrl>("movieserver/obs/signedUrl", content);
        }

        private static async Task<UploadPartRes> DoUploadPartFile(ObsSignedUrl url, string filePath, int partNumber, long segmentSize, long chunkSize)
        {
            using (var httpClient = new HttpClient())
            {
                httpClient.Timeout = TimeSpan.FromHours(2); // 设置长时间超时
                long offset = (partNumber - 1) * segmentSize;

                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    fileStream.Seek(offset, SeekOrigin.Begin);

                    // 使用 StreamContent 进行流式上传
                    using (var streamContent = new StreamContent(new ChunkStream(fileStream, offset, chunkSize), 8192))
                    {
                        if(!url.IsHuaweiServer()) // 不是华为云。目前这里就是指腾讯云。因为后续都用腾讯云，所以默认用腾讯云逻辑
                        {
                            streamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
                            streamContent.Headers.ContentLength = chunkSize;
                        }
                        

                        var response = await httpClient.PutAsync(url.Url, streamContent);

                        UploadPartRes res = new UploadPartRes();
                        res.Part = partNumber;
                        if (response.IsSuccessStatusCode)
                        {
                            var eTag = response.Headers.ETag?.Tag;
                            if (!string.IsNullOrWhiteSpace(eTag))
                            {
                                res.ETag = eTag; // eTag.Trim('"', '“', '”');
                                res.Success = true;
                            }
                            return res;
                        }
                        else if (response.StatusCode == HttpStatusCode.RequestTimeout)
                        {
                            res.Retry = true;
                            return res;
                        }
                        else
                        {
                            var errorContent = await response.Content.ReadAsStringAsync();
                            res.message = errorContent;
                            return res;
                        }
                    }
                }
            }
        }
        public static async Task<Retryable> CompleteUploadFile(ObsSignedUrl url, string uploadId, List<UploadPartRes> parts)
        {
            SignUrlParam param = new SignUrlParam();
            param.Method = "POST";
            param.UrlId = url.Id;
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("uploadId", uploadId);
            param.Params = dic;
            Dictionary<string, string> headers = new Dictionary<string, string>();
            if(url.IsHuaweiServer())
            {
                headers.Add("Content-Type", "application/xml");
            }
            else
            {
                headers.Add("Content-Type", "application/xml; charset=utf-8");
            }
            param.Headers = headers;

            JsonContent content = JsonContent.Create(param);
            ObsSignedUrl completeUrl = await PostAsync<ObsSignedUrl>("movieserver/obs/signedUrl", content);
            Retryable retry;
            int times = 5;
            bool rty;
            do
            {
                retry = await DoCompleteUploadFile(completeUrl, parts);
                rty = !retry.Success && retry.Retry && times > 0;
                if (rty)
                {
                    await Task.Delay(1000);
                }
            } while (rty);
            return retry;
        }
        /// <summary>
        /// 合并请求段,xml格式如下：
        /// <CompleteMultipartUpload> 
        ///     <Part> 
        ///         <PartNumber>partNum</PartNumber> 
        ///         <ETag>etag</ETag> 
        ///     </Part> 
        ///     <Part> 
        ///         <PartNumber>partNum</PartNumber> 
        ///         <ETag>etag</ETag> 
        ///     </Part> 
        ///     <Part> 
        ///         <PartNumber>partNum</PartNumber> 
        ///         <ETag>etag</ETag> 
        ///     </Part> 
        /// </CompleteMultipartUpload>
        /// </summary>
        /// <param name="url"></param>
        /// <param name="parts"></param>
        /// <returns></returns>
        public static async Task<Retryable> DoCompleteUploadFile(ObsSignedUrl url,List<UploadPartRes> parts)
        {
            string complateBody = BuildCompleteBody(parts);

            using (var httpClient = new HttpClient())
            {
                httpClient.Timeout = TimeSpan.FromHours(1);
                StringContent content = new StringContent(complateBody, Encoding.UTF8);
                if (url.IsHuaweiServer())
                {
                    AddHeaders(url, content);
                }
                else
                {
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/xml") { CharSet = "utf-8" };
                }
                
                var response = await httpClient.PostAsync(url.Url, content);
                Retryable res = new Retryable();
                try
                {
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        res.Success = true;
                        return res;
                    }
                    else if (response.StatusCode == HttpStatusCode.RequestTimeout)
                    {
                        res.Retry = true;
                        return res;
                    }
                    else
                    {
                        var responseContent = await response.Content.ReadAsStringAsync();
                        string resp = responseContent;
                        res.message = resp;
                        return res;
                    }
                }
                catch (WebException e)
                {
                    using (Stream stream = e.Response.GetResponseStream())
                    {
                        string resp = Stream2Str(stream);
                        res.message = resp;
                    }
                    return res;
                }
                catch (Exception e)
                {
                    logger.Error(e, "合并分段失败");
                    res.message = "合并分段失败";
                    return res;
                }
            }
        }

        private static string BuildCompleteBody(List<UploadPartRes> parts)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            sb.Append("<CompleteMultipartUpload>");
            foreach (UploadPartRes part in parts)
            {
                sb.Append("<Part>");
                sb.Append("<PartNumber>");
                sb.Append(part.Part);
                sb.Append("</PartNumber>");
                sb.Append("<ETag>");
                sb.Append(part.ETag);
                sb.Append("</ETag>");
                sb.Append("</Part>");
            }
            sb.Append("</CompleteMultipartUpload>");
            return sb.ToString();
        }
        /// <summary>
        /// 删除分段上传文件任务
        /// </summary>
        private static async void DeleteUploadPartFile(ObsSignedUrl url, string uploadId)
        {
            SignUrlParam param = new SignUrlParam();
            param.Method = "DELETE";
            param.UrlId = url.Id;
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("uploadId", uploadId);
            param.Params = dic;

            JsonContent content = JsonContent.Create(param);
            ObsSignedUrl deleteUrl = await PostAsync<ObsSignedUrl>("movieserver/obs/signedUrl", content);
            if(deleteUrl != null)
            {
                await DoDeleteUploadPartFile(deleteUrl);
            }
        }
        private static Task<bool> DoDeleteUploadPartFile(ObsSignedUrl url)
        {
            return Task.Factory.StartNew(() => {
                HttpWebRequest req = WebRequest.Create(url.Url) as HttpWebRequest;
                req.Method = "DELETE";
                req.Timeout = 3600000; //1小时超时
                AddHeaders(url, req);
                HttpWebResponse webResponse = null;
                try
                {
                    webResponse = req.GetResponse() as HttpWebResponse;
                    return webResponse.StatusCode == HttpStatusCode.NoContent;
                }
                catch (WebException ex)
                {
                    logger.Error(ex, "删除分段[url={0}]失败", url.Url);
                    return false;
                }
            });
        }
        private static void AddHeaders(ObsSignedUrl url, HttpWebRequest req)
        {
            if (url.Headers != null)
            {
                foreach (KeyValuePair<string, string> header in url.Headers)
                {
                    if (!header.Key.Equals("host", StringComparison.OrdinalIgnoreCase))
                    {
                        req.Headers.Add(header.Key, header.Value);
                    }
                }
            }
        }
        private static void AddHeaders(ObsSignedUrl url, HttpContent content, bool force = true)
        {
            if(url.Headers != null)
            {
                foreach (KeyValuePair<string, string> header in url.Headers)
                {
                    if (!header.Key.Equals("host", StringComparison.OrdinalIgnoreCase))
                    {
                        if (content.Headers.Contains(header.Key))
                        {
                            if (force)
                            {
                                content.Headers.Remove(header.Key);
                                content.Headers.Add(header.Key, header.Value);
                            }
                        }
                        else
                        {
                            content.Headers.Add(header.Key, header.Value);
                        }
                    }
                }
            }
        }
        private static string Stream2Str(Stream stream)
        {
            using (MemoryStream dest = new MemoryStream())
            {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    dest.Write(buffer, 0, bytesRead);
                }
                return Encoding.UTF8.GetString(dest.ToArray());
            }
        }
        public static async Task<bool> DownloadObsFile(string signedAuthUrl, string filePath)
        {
            ObsSignedUrl url = await GetAsync<ObsSignedUrl>(signedAuthUrl);
            if (url != null && url.Exist && !string.IsNullOrWhiteSpace(url.Url))
            {
                return await DownloadObsFile(url, filePath);
            }
            return false;
        }
        public static Task<bool> DownloadObsFile(ObsSignedUrl url, string filePath)
        {
            return Task.Factory.StartNew(() => {
                HttpWebRequest req = WebRequest.Create(url.Url) as HttpWebRequest;
                req.Method = "GET";
                req.Timeout = 3600000; //1小时超时
                AddHeaders(url, req);
                HttpWebResponse webResponse = null;
                try
                {
                    webResponse = req.GetResponse() as HttpWebResponse;
                    DirectoryInfo dir = Directory.GetParent(filePath);
                    if (dir != null)
                    {
                        if (!dir.Exists)
                        {
                            dir.Create();
                        }
                    }
                    using (Stream fs = File.OpenWrite(filePath))
                    {
                        using (Stream stream = webResponse.GetResponseStream())
                        {
                            byte[] buffer = new byte[8192];
                            int bytesRead;
                            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                fs.Write(buffer, 0, bytesRead);
                            }
                            return true;
                        }
                    }
                }
                catch (WebException ex)
                {
                    webResponse = ex.Response as HttpWebResponse;
                    using (MemoryStream dest = new MemoryStream())
                    {
                        using (Stream stream = webResponse.GetResponseStream())
                        {
                            byte[] buffer = new byte[8192];
                            int bytesRead;
                            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                dest.Write(buffer, 0, bytesRead);
                            }

                        }
                        string res = Encoding.UTF8.GetString(dest.ToArray());
                        if (!string.IsNullOrWhiteSpace(res))
                        {
                            MessageBox.Show(res);
                        }
                    }
                }
                return webResponse.StatusCode != HttpStatusCode.OK;
            });
        }
    }
}
